import os
import subprocess
import sys
import tempfile
import traceback

import uiplatform

NATT_MODES = ["natt", "none", "force-natt", "cisco-udp"]
VENDORS = ["cisco", "netscreen"]
DH_GROUPS = ["dh1", "dh2", "dh5"]
PFS_GROUPS = ["nopfs", "dh1", "dh2", "dh5", "server"]
IF_MODES = ["tun", "tap"]
AUTH_MODES = ["psk", "cert", "hybrid"]

def connect(connection):
    """Save connection parameters to a config file, connect using vpnc"""
    ret = False
    try:
        handle, tmp = tempfile.mkstemp()
        os.write(handle,
                 ("IPSec gateway %s\n" +
                  "IPSec ID %s\n" +
                  "IPSec secret %s\n" +
                  "Xauth username %s\n" + 
                  "Xauth password %s\n" +
                  "Domain %s\n" +
                  "NAT Traversal Mode %s\n" +
                  "Vendor %s\n" +
                  "IKE DH Group %s\n" +
                  "Perfect Forward Secrecy %s\n" + 
                  "Interface mode %s\n" +
                  "Local Addr %s\n" +
                  "Local Port %d\n" +
                  "Cisco UDP Encapsulation Port %d\n" +
                  "DPD idle timeout (our side) %d\n" +
                  "IKE Authmode %s\n" +
                  "IPSEC target network %s\n"
                 ) % \
                 (connection["gateway"],
                  connection["group"],
                  connection["groupPassword"],
                  connection["user"],
                  connection["userPassword"],
                  connection["domain"],
                  NATT_MODES[connection["nattMode"]],
                  VENDORS[connection["vendor"]],
                  DH_GROUPS[connection["dh"]],
                  PFS_GROUPS[connection["pfs"]],
                  IF_MODES[connection["ifMode"]],
                  connection["localAddress"],
                  connection["localPort"],
                  connection["localUdpPort"],
                  connection["dpdIdle"],
                  AUTH_MODES[connection["authMode"]],
                  connection["targetNetwork"]
                 ))
        if connection["esd"]:
            os.write(handle, "Enable Single DES\n")
        if connection["ene"]:
            os.write(handle, "Enable no encryption\n")
        if connection["caFile"] != "":
            os.write(handle, "CA-File %s\n" % connection["caFile"])
        if connection["caDir"] != "":
            os.write(handle, "CA-Dir %s\n" % connection["caDir"])
        if connection["script"] != "":
            os.write(handle, "Script %s\n" % connection["script"])
        os.close(handle)
        ret = sudo(["vpnc", "--non-inter", tmp])
    finally:
        try:
            os.remove(tmp)
        except:
            traceback.print_exc()
    return ret
    

def disconnect():
    """Disconnect current vpnc connection"""
    try:
        sudo(["vpnc-disconnect"])
    except:
        traceback.print_exc()
        

def getStatus():
    if uiplatform.name == "mac":
        p = subprocess.Popen("/sbin/ifconfig", 
                             stdout=subprocess.PIPE, stderr=subprocess.STDOUT)
        for line in readPipe(p.stdout).split("\n"):
            if (line[0:3] == "tun") and (line.find("RUNNING") >= 0):
                return "connected"
    else:
        p = subprocess.Popen(["/sbin/ifconfig", "tun0"],
                             stdout=subprocess.PIPE, stderr=subprocess.STDOUT)
        if readPipe(p.stdout).find("RUNNING") >= 0:
            return "connected"
    return "disconnected"

        
def sudo(argList):
    """Run command as root, return True (success) or False"""

    ret = False

    if not os.environ.has_key("PATH"):
        os.environ["PATH"] = ""
    os.environ["PATH"] += os.pathsep + "/opt/local/sbin" + \
                          os.pathsep + "/opt/local/bin" + \
                          os.pathsep + "/usr/local/sbin" + \
                          os.pathsep + "/usr/local/bin"
    
    if uiplatform.name == "mac":
        # On Mac, try sudo first, then create and execute a script 
        # with authorization
        import copy
        sudoArgs = copy.copy(argList)
        sudoArgs.insert(0, "sudo")
        sudoArgs.insert(1, "-S")
        try:
            p = subprocess.Popen(sudoArgs)
            if waitProc(p) == 0:
                ret = True
        except:
            pass
        
        if ret == False:
            from Authorization import Authorization, \
                kAuthorizationFlagDestroyRights
            auth = \
                Authorization(destroyflags=(kAuthorizationFlagDestroyRights,))
            handle, cmd = tempfile.mkstemp(".py")
            os.write(handle, 
                     "#!" + sys.executable + "\n" +
                     "import os\n" +
                     "import subprocess\n" +
                     "os.setuid(0)\n" +
                     "args = [")
            for arg in argList:
                os.write(handle, "'%s', " % arg)
            os.write(handle, 
                     "]\n" + 
                     "p = subprocess.Popen(args)\n"
                     "if p.wait() == 0:\n" +
                     "    print 'OK'\n")
            os.close(handle)
            os.chmod(cmd, 0700)
            try:
                pipe = auth.executeWithPrivileges(cmd)
                if readPipe(pipe).find("OK") >= 0:
                    ret = True
                pipe.close()
            except:
                traceback.print_exc()
            finally:
                os.remove(cmd)

    else:
        # On other platforms, run command using sudo/gtksu/kdesu etc. 
        if uiplatform.name == "gnome":
            argList.insert(0, "gksudo")
            argList.insert(1, "--")
        elif uiplatform.name == "kde":
            argList.insert(0, "kdesu")
        else:
            argList.insert(0, "sudo")
            argList.insert(1, "-S")
        p = subprocess.Popen(argList, executable=argList[0])
        if waitProc(p) == 0:
            ret = True
            
    return ret


def readPipe(p):
    """Read from a pipe, retry on EINTR"""
    ret = ""
    try:
        ret = p.read()
    except IOError, e:
        import errno
        if e.errno == errno.EINTR:
            # Interrupted system call: try again
            ret = p.read()
    return ret


def waitProc(p):
    """Wait for a process to finish, return exit status"""
    ret = -1
    try:
        ret = p.wait()
    except OSError, e:
        import errno
        if e.errno == errno.EINTR:
            # Interrupted system call: try again
            ret = p.wait()
    return ret
